/*
 * @(#)MetalTabbedPaneUI.java	1.31 03/01/23
 *
 * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
 * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */

package lookandfeel.Dmetal;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Insets;
import java.awt.LayoutManager;
import java.awt.Rectangle;

import javax.swing.Icon;
import javax.swing.JComponent;
import javax.swing.JTabbedPane;
import javax.swing.UIManager;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicTabbedPaneUI;

/**
 * The Metal subclass of BasicTabbedPaneUI.
 * <p>
 * <strong>Warning:</strong> Serialized objects of this class will not be
 * compatible with future Swing releases. The current serialization support is
 * appropriate for short term storage or RMI between applications running the
 * same version of Swing. As of 1.4, support for long term storage of all
 * JavaBeans<sup><font size="-2">TM</font></sup> has been added to the
 * <code>java.beans</code> package. Please see {@link java.beans.XMLEncoder}.
 * 
 * @version 1.19 08/28/98
 * @author Tom Santos
 */

public class MetalTabbedPaneUI extends BasicTabbedPaneUI
{

	protected int minTabWidth = 40;

	protected Color tabAreaBackground;

	protected Color selectColor;

	protected Color selectHighlight;

	public static ComponentUI createUI(JComponent x)
	{
		return new MetalTabbedPaneUI();
	}

	@Override
	protected LayoutManager createLayoutManager()
	{
		if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT)
			return super.createLayoutManager();
		return new TabbedPaneLayout();
	}

	@Override
	protected void installDefaults()
	{
		super.installDefaults();

		tabAreaBackground = UIManager.getColor("TabbedPane.tabAreaBackground");
		selectColor = UIManager.getColor("TabbedPane.selected");
		selectHighlight = UIManager.getColor("TabbedPane.selectHighlight");
	}

	@Override
	protected void paintTabBorder(Graphics g, int tabPlacement, int tabIndex,
			int x, int y, int w, int h, boolean isSelected)
	{
		int bottom = y + ( h - 1 );
		int right = x + ( w - 1 );

		switch (tabPlacement)
		{
			case LEFT:
				paintLeftTabBorder(tabIndex, g, x, y, w, h, bottom, right,
						isSelected);
				break;
			case BOTTOM:
				paintBottomTabBorder(tabIndex, g, x, y, w, h, bottom, right,
						isSelected);
				break;
			case RIGHT:
				paintRightTabBorder(tabIndex, g, x, y, w, h, bottom, right,
						isSelected);
				break;
			case TOP:
			default:
				paintTopTabBorder(tabIndex, g, x, y, w, h, bottom, right,
						isSelected);
		}
	}

	protected void paintTopTabBorder(int tabIndex, Graphics g, int x, int y,
			int w, int h, int btm, int rght, boolean isSelected)
	{
		int currentRun = getRunForTab(tabPane.getTabCount(), tabIndex);
		int lastIndex = lastTabInRun(tabPane.getTabCount(), currentRun);
		int firstIndex = tabRuns[currentRun];
		boolean leftToRight = MetalUtils.isLeftToRight(tabPane);
		int bottom = h - 1;
		int right = w - 1;

		//
		// Paint Gap
		//

		if (shouldFillGap(currentRun, tabIndex, x, y))
		{
			g.translate(x, y);

			if (leftToRight)
			{
				g.setColor(getColorForGap(currentRun, x, y + 1));
				g.fillRect(1, 0, 5, 3);
				g.fillRect(1, 3, 2, 2);
			}
			else
			{
				g.setColor(getColorForGap(currentRun, x + w - 1, y + 1));
				g.fillRect(right - 5, 0, 5, 3);
				g.fillRect(right - 2, 3, 2, 2);
			}

			g.translate(-x, -y);
		}

		g.translate(x, y);

		//
		// Paint Border
		//

		g.setColor(darkShadow);

		if (leftToRight)
		{

			// Paint slant
			g.drawLine(1, 5, 6, 0);

			// Paint top
			g.drawLine(6, 0, right, 0);

			// Paint right
			if (tabIndex == lastIndex) // last tab in run
				g.drawLine(right, 1, right, bottom);

			// Paint left
			if (tabIndex != tabRuns[runCount - 1]) // not the first tab in the
				// last run
				g.drawLine(0, 0, 0, bottom);
			else // the first tab in the last run
			g.drawLine(0, 6, 0, bottom);
		}
		else
		{

			// Paint slant
			g.drawLine(right - 1, 5, right - 6, 0);

			// Paint top
			g.drawLine(right - 6, 0, 0, 0);

			// Paint right
			if (tabIndex != tabRuns[runCount - 1]) // not the first tab in the
				// last run
				g.drawLine(right, 0, right, bottom);
			else // the first tab in the last run
			g.drawLine(right, 6, right, bottom);

			// Paint left
			if (tabIndex == lastIndex) // last tab in run
				g.drawLine(0, 1, 0, bottom);
		}

		//
		// Paint Highlight
		//

		g.setColor(isSelected ? selectHighlight : highlight);

		if (leftToRight)
		{

			// Paint slant
			g.drawLine(1, 6, 6, 1);

			// Paint top
			g.drawLine(6, 1, right, 1);

			// Paint left
			g.drawLine(1, 6, 1, bottom);

			// paint highlight in the gap on tab behind this one
			// on the left end (where they all line up)
			if (( tabIndex == firstIndex )
					&& ( tabIndex != tabRuns[runCount - 1] ))
			{
				// first tab in run but not first tab in last run
				if (tabPane.getSelectedIndex() == tabRuns[currentRun + 1]) // tab
					// in
					// front
					// of
					// selected
					// tab
					g.setColor(selectHighlight);
				else // tab in front of normal tab
				g.setColor(highlight);
				g.drawLine(1, 0, 1, 4);
			}
		}
		else
		{

			// Paint slant
			g.drawLine(right - 1, 6, right - 6, 1);

			// Paint top
			g.drawLine(right - 6, 1, 1, 1);

			// Paint left
			if (tabIndex == lastIndex) // last tab in run
				g.drawLine(1, 1, 1, bottom);
			else g.drawLine(0, 1, 0, bottom);
		}

		g.translate(-x, -y);
	}

	protected boolean shouldFillGap(int currentRun, int tabIndex, int x, int y)
	{
		boolean result = false;

		if (currentRun == runCount - 2)
		{ // If it's the second to last row.
			Rectangle lastTabBounds = getTabBounds(tabPane, tabPane
					.getTabCount() - 1);
			Rectangle tabBounds = getTabBounds(tabPane, tabIndex);
			if (MetalUtils.isLeftToRight(tabPane))
			{
				int lastTabRight = lastTabBounds.x + lastTabBounds.width - 1;

				// is the right edge of the last tab to the right
				// of the left edge of the current tab?
				if (lastTabRight > tabBounds.x + 2) return true;
			}
			else
			{
				int lastTabLeft = lastTabBounds.x;
				int currentTabRight = tabBounds.x + tabBounds.width - 1;

				// is the left edge of the last tab to the left
				// of the right edge of the current tab?
				if (lastTabLeft < currentTabRight - 2) return true;
			}
		}
		else // fill in gap for all other rows except last row
		result = currentRun != runCount - 1;

		return result;
	}

	protected Color getColorForGap(int currentRun, int x, int y)
	{
		final int shadowWidth = 4;
		int selectedIndex = tabPane.getSelectedIndex();
		int startIndex = tabRuns[currentRun + 1];
		int endIndex = lastTabInRun(tabPane.getTabCount(), currentRun + 1);
		// int tabOverGap = -1;
		// Check each tab in the row that is 'on top' of this row
		for (int i = startIndex; i <= endIndex; ++i)
		{
			Rectangle tabBounds = getTabBounds(tabPane, i);
			int tabLeft = tabBounds.x;
			int tabRight = ( tabBounds.x + tabBounds.width ) - 1;
			// Check to see if this tab is over the gap
			if (MetalUtils.isLeftToRight(tabPane))
			{
				if (( tabLeft <= x ) && ( tabRight - shadowWidth > x ))
					return selectedIndex == i ? selectColor : tabPane
							.getBackgroundAt(i);
			}
			else if (( tabLeft + shadowWidth < x ) && ( tabRight >= x ))
				return selectedIndex == i ? selectColor : tabPane
						.getBackgroundAt(i);
		}

		return tabPane.getBackground();
	}

	protected void paintLeftTabBorder(int tabIndex, Graphics g, int x, int y,
			int w, int h, int btm, int rght, boolean isSelected)
	{
		int tabCount = tabPane.getTabCount();
		int currentRun = getRunForTab(tabCount, tabIndex);
		int lastIndex = lastTabInRun(tabCount, currentRun);
		int firstIndex = tabRuns[currentRun];

		g.translate(x, y);

		int bottom = h - 1;
		int right = w - 1;

		//
		// Paint part of the tab above
		//

		if (tabIndex != firstIndex)
		{
			g.setColor(tabPane.getSelectedIndex() == tabIndex - 1 ? selectColor
					: tabPane.getBackgroundAt(tabIndex - 1));
			g.fillRect(2, 0, 4, 3);
			g.drawLine(2, 3, 2, 3);
		}

		//
		// Paint Highlight
		//

		g.setColor(isSelected ? selectHighlight : highlight);

		// Paint slant
		g.drawLine(1, 6, 6, 1);

		// Paint top
		g.drawLine(6, 1, right, 1);

		// Paint left
		g.drawLine(1, 6, 1, bottom);

		if (tabIndex != firstIndex)
		{
			g
					.setColor(tabPane.getSelectedIndex() == tabIndex - 1 ? selectHighlight
							: highlight);
			g.drawLine(1, 0, 1, 4);
		}

		//
		// Paint Border
		//

		g.setColor(darkShadow);

		// Paint slant
		g.drawLine(1, 5, 6, 0);

		// Paint top
		g.drawLine(6, 0, right, 0);

		// Paint left
		if (tabIndex != firstIndex)
			g.drawLine(0, 0, 0, bottom);
		else g.drawLine(0, 6, 0, bottom);

		// Paint bottom
		if (tabIndex == lastIndex) g.drawLine(0, bottom, right, bottom);

		g.translate(-x, -y);
	}

	protected void paintBottomTabBorder(int tabIndex, Graphics g, int x, int y,
			int w, int h, int btm, int rght, boolean isSelected)
	{
		int tabCount = tabPane.getTabCount();
		int currentRun = getRunForTab(tabCount, tabIndex);
		int lastIndex = lastTabInRun(tabCount, currentRun);
		int firstIndex = tabRuns[currentRun];
		boolean leftToRight = MetalUtils.isLeftToRight(tabPane);

		int bottom = h - 1;
		int right = w - 1;

		//
		// Paint Gap
		//

		if (shouldFillGap(currentRun, tabIndex, x, y))
		{
			g.translate(x, y);

			if (leftToRight)
			{
				g.setColor(getColorForGap(currentRun, x, y));
				g.fillRect(1, bottom - 4, 3, 5);
				g.fillRect(4, bottom - 1, 2, 2);
			}
			else
			{
				g.setColor(getColorForGap(currentRun, x + w - 1, y));
				g.fillRect(right - 3, bottom - 3, 3, 4);
				g.fillRect(right - 5, bottom - 1, 2, 2);
				g.drawLine(right - 1, bottom - 4, right - 1, bottom - 4);
			}

			g.translate(-x, -y);
		}

		g.translate(x, y);

		//
		// Paint Border
		//

		g.setColor(darkShadow);

		if (leftToRight)
		{

			// Paint slant
			g.drawLine(1, bottom - 5, 6, bottom);

			// Paint bottom
			g.drawLine(6, bottom, right, bottom);

			// Paint right
			if (tabIndex == lastIndex) g.drawLine(right, 0, right, bottom);

			// Paint left
			if (tabIndex != tabRuns[runCount - 1])
				g.drawLine(0, 0, 0, bottom);
			else g.drawLine(0, 0, 0, bottom - 6);
		}
		else
		{

			// Paint slant
			g.drawLine(right - 1, bottom - 5, right - 6, bottom);

			// Paint bottom
			g.drawLine(right - 6, bottom, 0, bottom);

			// Paint right
			if (tabIndex != tabRuns[runCount - 1]) // not the first tab in the
				// last run
				g.drawLine(right, 0, right, bottom);
			else // the first tab in the last run
			g.drawLine(right, 0, right, bottom - 6);

			// Paint left
			if (tabIndex == lastIndex) // last tab in run
				g.drawLine(0, 0, 0, bottom);
		}

		//
		// Paint Highlight
		//

		g.setColor(isSelected ? selectHighlight : highlight);

		if (leftToRight)
		{

			// Paint slant
			g.drawLine(1, bottom - 6, 6, bottom - 1);

			// Paint left
			g.drawLine(1, 0, 1, bottom - 6);

			// paint highlight in the gap on tab behind this one
			// on the left end (where they all line up)
			if (( tabIndex == firstIndex )
					&& ( tabIndex != tabRuns[runCount - 1] ))
			{
				// first tab in run but not first tab in last run
				if (tabPane.getSelectedIndex() == tabRuns[currentRun + 1]) // tab
					// in
					// front
					// of
					// selected
					// tab
					g.setColor(selectHighlight);
				else // tab in front of normal tab
				g.setColor(highlight);
				g.drawLine(1, bottom - 4, 1, bottom);
			}
		}
		else // Paint left
		if (tabIndex == lastIndex) // last tab in run
			g.drawLine(1, 0, 1, bottom - 1);
		else g.drawLine(0, 0, 0, bottom - 1);

		g.translate(-x, -y);
	}

	protected void paintRightTabBorder(int tabIndex, Graphics g, int x, int y,
			int w, int h, int btm, int rght, boolean isSelected)
	{
		int tabCount = tabPane.getTabCount();
		int currentRun = getRunForTab(tabCount, tabIndex);
		int lastIndex = lastTabInRun(tabCount, currentRun);
		int firstIndex = tabRuns[currentRun];

		g.translate(x, y);

		int bottom = h - 1;
		int right = w - 1;

		//
		// Paint part of the tab above
		//

		if (tabIndex != firstIndex)
		{
			g
					.setColor(tabPane.getSelectedIndex() == tabIndex - 1 ? tabAreaBackground
							: tabPane.getBackgroundAt(tabIndex - 1));
			g.fillRect(right - 5, 0, 5, 3);
			g.fillRect(right - 2, 3, 2, 2);
		}

		//
		// Paint Highlight
		//

		g.setColor(isSelected ? selectHighlight : highlight);

		// Paint slant
		g.drawLine(right - 6, 1, right - 1, 6);

		// Paint top
		g.drawLine(0, 1, right - 6, 1);

		// Paint left
		if (!isSelected) g.drawLine(0, 1, 0, bottom);

		//
		// Paint Border
		//

		g.setColor(darkShadow);

		// Paint slant
		g.drawLine(right - 6, 0, right, 6);

		// Paint top
		g.drawLine(0, 0, right - 6, 0);

		// Paint right
		if (tabIndex != firstIndex)
			g.drawLine(right, 0, right, bottom);
		else g.drawLine(right, 6, right, bottom);

		// Paint bottom
		if (tabIndex == lastIndex) g.drawLine(0, bottom, right, bottom);

		g.translate(-x, -y);
	}

	@Override
	public void update(Graphics g, JComponent c)
	{
		if (c.isOpaque())
		{
			g.setColor(tabAreaBackground);
			g.fillRect(0, 0, c.getWidth(), c.getHeight());
		}
		paint(g, c);
	}

	@Override
	protected void paintTabBackground(Graphics g, int tabPlacement,
			int tabIndex, int x, int y, int w, int h, boolean isSelected)
	{
		// int slantWidth = h / 2;
		if (isSelected)
			g.setColor(selectColor);
		else g.setColor(tabPane.getBackgroundAt(tabIndex));

		if (MetalUtils.isLeftToRight(tabPane))
			switch (tabPlacement)
			{
				case LEFT:
					g.fillRect(x + 5, y + 1, w - 5, h - 1);
					g.fillRect(x + 2, y + 4, 3, h - 4);
					break;
				case BOTTOM:
					g.fillRect(x + 2, y, w - 2, h - 4);
					g.fillRect(x + 5, y + ( h - 1 ) - 3, w - 5, 3);
					break;
				case RIGHT:
					g.fillRect(x + 1, y + 1, w - 5, h - 1);
					g.fillRect(x + ( w - 1 ) - 3, y + 5, 3, h - 5);
					break;
				case TOP:
				default:
					g.fillRect(x + 4, y + 2, ( w - 1 ) - 3, ( h - 1 ) - 1);
					g.fillRect(x + 2, y + 5, 2, h - 5);
			}
		else switch (tabPlacement)
		{
			case LEFT:
				g.fillRect(x + 5, y + 1, w - 5, h - 1);
				g.fillRect(x + 2, y + 4, 3, h - 4);
				break;
			case BOTTOM:
				g.fillRect(x, y, w - 5, h - 1);
				g.fillRect(x + ( w - 1 ) - 4, y, 4, h - 5);
				g.fillRect(x + ( w - 1 ) - 4, y + ( h - 1 ) - 4, 2, 2);
				break;
			case RIGHT:
				g.fillRect(x + 1, y + 1, w - 5, h - 1);
				g.fillRect(x + ( w - 1 ) - 3, y + 5, 3, h - 5);
				break;
			case TOP:
			default:
				g.fillRect(x, y + 2, ( w - 1 ) - 3, ( h - 1 ) - 1);
				g.fillRect(x + ( w - 1 ) - 3, y + 4, 3, h - 4);
		}
	}

	/**
	 * Overridden to do nothing for the Java L&F.
	 */
	@Override
	protected int getTabLabelShiftX(int tabPlacement, int tabIndex,
			boolean isSelected)
	{
		return 0;
	}

	/**
	 * Overridden to do nothing for the Java L&F.
	 */
	@Override
	protected int getTabLabelShiftY(int tabPlacement, int tabIndex,
			boolean isSelected)
	{
		return 0;
	}

	@Override
	public void paint(Graphics g, JComponent c)
	{
		int tabPlacement = tabPane.getTabPlacement();

		Insets insets = c.getInsets();
		Dimension size = c.getSize();

		// Paint the background for the tab area
		if (tabPane.isOpaque())
		{
			g.setColor(c.getBackground());
			switch (tabPlacement)
			{
				case LEFT:
					g.fillRect(insets.left, insets.top, calculateTabAreaWidth(
							tabPlacement, runCount, maxTabWidth), size.height
							- insets.bottom - insets.top);
					break;
				case BOTTOM:
					int totalTabHeight = calculateTabAreaHeight(tabPlacement,
							runCount, maxTabHeight);
					g.fillRect(insets.left, size.height - insets.bottom
							- totalTabHeight, size.width - insets.left
							- insets.right, totalTabHeight);
					break;
				case RIGHT:
					int totalTabWidth = calculateTabAreaWidth(tabPlacement,
							runCount, maxTabWidth);
					g.fillRect(size.width - insets.right - totalTabWidth,
							insets.top, totalTabWidth, size.height - insets.top
									- insets.bottom);
					break;
				case TOP:
				default:
					g.fillRect(insets.left, insets.top, size.width
							- insets.right - insets.left,
							calculateTabAreaHeight(tabPlacement, runCount,
									maxTabHeight));
					paintHighlightBelowTab();
			}
		}

		super.paint(g, c);
	}

	protected void paintHighlightBelowTab()
	{

	}

	@Override
	protected void paintFocusIndicator(Graphics g, int tabPlacement,
			Rectangle[] rects, int tabIndex, Rectangle iconRect,
			Rectangle textRect, boolean isSelected)
	{
		if (tabPane.hasFocus() && isSelected)
		{
			Rectangle tabRect = rects[tabIndex];
			boolean lastInRun = isLastInRun(tabIndex);
			g.setColor(focus);
			g.translate(tabRect.x, tabRect.y);
			int right = tabRect.width - 1;
			int bottom = tabRect.height - 1;
			boolean leftToRight = MetalUtils.isLeftToRight(tabPane);
			switch (tabPlacement)
			{
				case RIGHT:
					g.drawLine(right - 6, 2, right - 2, 6); // slant
					g.drawLine(1, 2, right - 6, 2); // top
					g.drawLine(right - 2, 6, right - 2, bottom); // right
					g.drawLine(1, 2, 1, bottom); // left
					g.drawLine(1, bottom, right - 2, bottom); // bottom
					break;
				case BOTTOM:
					if (leftToRight)
					{
						g.drawLine(2, bottom - 6, 6, bottom - 2); // slant
						g.drawLine(6, bottom - 2, right, bottom - 2); // bottom
						g.drawLine(2, 0, 2, bottom - 6); // left
						g.drawLine(2, 0, right, 0); // top
						g.drawLine(right, 0, right, bottom - 2); // right
					}
					else
					{
						g
								.drawLine(right - 2, bottom - 6, right - 6,
										bottom - 2); // slant
						g.drawLine(right - 2, 0, right - 2, bottom - 6); // right
						if (lastInRun)
						{
							// last tab in run
							g.drawLine(2, bottom - 2, right - 6, bottom - 2); // bottom
							g.drawLine(2, 0, right - 2, 0); // top
							g.drawLine(2, 0, 2, bottom - 2); // left
						}
						else
						{
							g.drawLine(1, bottom - 2, right - 6, bottom - 2); // bottom
							g.drawLine(1, 0, right - 2, 0); // top
							g.drawLine(1, 0, 1, bottom - 2); // left
						}
					}
					break;
				case LEFT:
					g.drawLine(2, 6, 6, 2); // slant
					g.drawLine(2, 6, 2, bottom - 1); // left
					g.drawLine(6, 2, right, 2); // top
					g.drawLine(right, 2, right, bottom - 1); // right
					g.drawLine(2, bottom - 1, right, bottom - 1); // bottom
					break;
				case TOP:
				default:
					if (leftToRight)
					{
						g.drawLine(2, 6, 6, 2); // slant
						g.drawLine(2, 6, 2, bottom - 1); // left
						g.drawLine(6, 2, right, 2); // top
						g.drawLine(right, 2, right, bottom - 1); // right
						g.drawLine(2, bottom - 1, right, bottom - 1); // bottom
					}
					else
					{
						g.drawLine(right - 2, 6, right - 6, 2); // slant
						g.drawLine(right - 2, 6, right - 2, bottom - 1); // right
						if (lastInRun)
						{
							// last tab in run
							g.drawLine(right - 6, 2, 2, 2); // top
							g.drawLine(2, 2, 2, bottom - 1); // left
							g.drawLine(right - 2, bottom - 1, 2, bottom - 1); // bottom
						}
						else
						{
							g.drawLine(right - 6, 2, 1, 2); // top
							g.drawLine(1, 2, 1, bottom - 1); // left
							g.drawLine(right - 2, bottom - 1, 1, bottom - 1); // bottom
						}
					}
			}
			g.translate(-tabRect.x, -tabRect.y);
		}
	}

	@Override
	protected void paintContentBorderTopEdge(Graphics g, int tabPlacement,
			int selectedIndex, int x, int y, int w, int h)
	{
		boolean leftToRight = MetalUtils.isLeftToRight(tabPane);
		int right = x + w - 1;
		Rectangle selRect = selectedIndex < 0 ? null : getTabBounds(
				selectedIndex, calcRect);
		g.setColor(selectHighlight);

		// Draw unbroken line if tabs are not on TOP, OR
		// selected tab is not in run adjacent to content, OR
		// selected tab is not visible (SCROLL_TAB_LAYOUT)
		//
		if (( tabPlacement != TOP ) || ( selectedIndex < 0 )
				|| ( selRect.y + selRect.height + 1 < y )
				|| ( ( selRect.x < x ) || ( selRect.x > x + w ) ))
			g.drawLine(x, y, x + w - 2, y);
		else
		{
			// Break line to show visual connection to selected tab
			boolean lastInRun = isLastInRun(selectedIndex);

			if (leftToRight || lastInRun)
				g.drawLine(x, y, selRect.x + 1, y);
			else g.drawLine(x, y, selRect.x, y);

			if (selRect.x + selRect.width < right - 1)
			{
				if (leftToRight && !lastInRun)
					g.drawLine(selRect.x + selRect.width, y, right - 1, y);
				else g.drawLine(selRect.x + selRect.width - 1, y, right - 1, y);
			}
			else
			{
				g.setColor(shadow);
				g.drawLine(x + w - 2, y, x + w - 2, y);
			}
		}
	}

	@Override
	protected void paintContentBorderBottomEdge(Graphics g, int tabPlacement,
			int selectedIndex, int x, int y, int w, int h)
	{
		boolean leftToRight = MetalUtils.isLeftToRight(tabPane);
		int bottom = y + h - 1;
		int right = x + w - 1;
		Rectangle selRect = selectedIndex < 0 ? null : getTabBounds(
				selectedIndex, calcRect);
		g.setColor(shadow);

		// Draw unbroken line if tabs are not on BOTTOM, OR
		// selected tab is not in run adjacent to content, OR
		// selected tab is not visible (SCROLL_TAB_LAYOUT)
		//
		if (( tabPlacement != BOTTOM ) || ( selectedIndex < 0 )
				|| ( selRect.y - 1 > h )
				|| ( ( selRect.x < x ) || ( selRect.x > x + w ) ))
		{
			g.setColor(darkShadow);
			g.drawLine(x, y + h - 1, x + w - 1, y + h - 1);
		}
		else
		{
			// Break line to show visual connection to selected tab
			boolean lastInRun = isLastInRun(selectedIndex);

			g.setColor(darkShadow);

			if (leftToRight || lastInRun)
				g.drawLine(x, bottom, selRect.x, bottom);
			else g.drawLine(x, bottom, selRect.x - 1, bottom);

			if (selRect.x + selRect.width < x + w - 2)
				if (leftToRight && !lastInRun)
					g
							.drawLine(selRect.x + selRect.width, bottom, right,
									bottom);
				else g.drawLine(selRect.x + selRect.width - 1, bottom, right,
						bottom);
		}
	}

	@Override
	protected void paintContentBorderLeftEdge(Graphics g, int tabPlacement,
			int selectedIndex, int x, int y, int w, int h)
	{
		Rectangle selRect = selectedIndex < 0 ? null : getTabBounds(
				selectedIndex, calcRect);
		g.setColor(selectHighlight);

		// Draw unbroken line if tabs are not on LEFT, OR
		// selected tab is not in run adjacent to content, OR
		// selected tab is not visible (SCROLL_TAB_LAYOUT)
		//
		if (( tabPlacement != LEFT ) || ( selectedIndex < 0 )
				|| ( selRect.x + selRect.width + 1 < x )
				|| ( ( selRect.y < y ) || ( selRect.y > y + h ) ))
			g.drawLine(x, y, x, y + h - 2);
		else
		{
			// Break line to show visual connection to selected tab
			g.drawLine(x, y, x, selRect.y + 1);
			if (selRect.y + selRect.height < y + h - 2)
				g.drawLine(x, selRect.y + selRect.height + 1, x, y + h + 2);
		}
	}

	@Override
	protected void paintContentBorderRightEdge(Graphics g, int tabPlacement,
			int selectedIndex, int x, int y, int w, int h)
	{
		Rectangle selRect = selectedIndex < 0 ? null : getTabBounds(
				selectedIndex, calcRect);
		g.setColor(shadow);

		// Draw unbroken line if tabs are not on RIGHT, OR
		// selected tab is not in run adjacent to content, OR
		// selected tab is not visible (SCROLL_TAB_LAYOUT)
		//
		if (( tabPlacement != RIGHT ) || ( selectedIndex < 0 )
				|| ( selRect.x - 1 > w )
				|| ( ( selRect.y < y ) || ( selRect.y > y + h ) ))
		{
			g.setColor(darkShadow);
			g.drawLine(x + w - 1, y, x + w - 1, y + h - 1);
		}
		else
		{
			// Break line to show visual connection to selected tab
			g.setColor(darkShadow);
			g.drawLine(x + w - 1, y, x + w - 1, selRect.y);

			if (selRect.y + selRect.height < y + h - 2)
			{
				g.setColor(darkShadow);
				g.drawLine(x + w - 1, selRect.y + selRect.height, x + w - 1, y
						+ h - 2);
			}
		}
	}

	@Override
	protected int calculateMaxTabHeight(int tabPlacement)
	{
		FontMetrics metrics = getFontMetrics();
		int height = metrics.getHeight();
		boolean tallerIcons = false;

		for (int i = 0; i < tabPane.getTabCount(); ++i)
		{
			Icon icon = tabPane.getIconAt(i);
			if (icon != null) if (icon.getIconHeight() > height)
			{
				tallerIcons = true;
				break;
			}
		}
		return super.calculateMaxTabHeight(tabPlacement)
				- ( tallerIcons ? ( tabInsets.top + tabInsets.bottom ) : 0 );
	}

	@Override
	protected int getTabRunOverlay(int tabPlacement)
	{
		// Tab runs laid out vertically should overlap
		// at least as much as the largest slant
		if (( tabPlacement == LEFT ) || ( tabPlacement == RIGHT ))
		{
			int maxTabHeight = calculateMaxTabHeight(tabPlacement);
			return maxTabHeight / 2;
		}
		return 0;
	}

	// Don't rotate runs!
	protected boolean shouldRotateTabRuns(int tabPlacement, int selectedRun)
	{
		return false;
	}

	// Don't pad last run
	@Override
	protected boolean shouldPadTabRun(int tabPlacement, int run)
	{
		return ( runCount > 1 ) && ( run < runCount - 1 );
	}

	private boolean isLastInRun(int tabIndex)
	{
		int run = getRunForTab(tabPane.getTabCount(), tabIndex);
		int lastIndex = lastTabInRun(tabPane.getTabCount(), run);
		return tabIndex == lastIndex;
	}

	/**
	 * This inner class is marked &quot;public&quot; due to a compiler bug. This
	 * class should be treated as a &quot;protected&quot; inner class.
	 * Instantiate it only within subclasses of MetalTabbedPaneUI.
	 */
	public class TabbedPaneLayout extends BasicTabbedPaneUI.TabbedPaneLayout
	{

		public TabbedPaneLayout()
		{
			MetalTabbedPaneUI.this.super();
		}

		@Override
		protected void normalizeTabRuns(int tabPlacement, int tabCount,
				int start, int max)
		{
			// Only normalize the runs for top & bottom; normalizing
			// doesn't look right for Metal's vertical tabs
			// because the last run isn't padded and it looks odd to have
			// fat tabs in the first vertical runs, but slimmer ones in the
			// last (this effect isn't noticeable for horizontal tabs).
			if (( tabPlacement == TOP ) || ( tabPlacement == BOTTOM ))
				super.normalizeTabRuns(tabPlacement, tabCount, start, max);
		}

		// Don't rotate runs!
		@Override
		protected void rotateTabRuns(int tabPlacement, int selectedRun)
		{
		}

		// Don't pad selected tab
		@Override
		protected void padSelectedTab(int tabPlacement, int selectedIndex)
		{
		}
	}

}
